package net.zoneland.zrdp.common.utils.ip;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;

import net.zoneland.zrdp.common.utils.ServletUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 获取IP方法
 *
 * @author zonevue
 */
public class IpUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(IpUtils.class);
    public static final String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
    // 匹配 ip
    public static final String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
    public static final String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
    // 匹配网段
    public static final String REGX_IP_SEG = "(" + REGX_IP + "-" + REGX_IP + ")";
    private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";
    private static final String LOCALHOST_IPV4 = "127.0.0.1";
    private static final String UNKNOWN = "unknown";
    // 使用数组列出所有可能包含客户端IP的头文件
    private static final String[] HEADERS = {
        "x-forwarded-for", "Proxy-Client-IP", "WL-Proxy-Client-IP", "X-Real-IP", "X-Forwarded-For"
    };

    private IpUtils() {
        throw new IllegalStateException("Utility class");
    }
    /**
     * 获取客户端IP
     *
     * @return IP地址
     */
    public static String getIpAddr() {
        return getIpAddr(ServletUtils.getRequest());
    }

    /**
     * 获取客户端IP
     *
     * @param request 请求对象
     * @return IP地址
     */
    public static String getIpAddr(final HttpServletRequest request) {
        if (request == null) {
            return UNKNOWN;
        }

        final String ip = request.getRemoteAddr();
        if (LOCALHOST_IPV6.equals(ip)) {
            return LOCALHOST_IPV4;
        }

        // 使用Stream API遍历头文件
        return Arrays.stream(HEADERS)
            .map(request::getHeader)
            .filter(IpUtils::isValidHeaderIp)
            .findFirst()
            .orElse(ip); // 如果没有找到有效的IP，则返回原始的RemoteAddr
    }

    /**
     * 检查是否为内部IP地址
     *
     * @param ip IP地址
     * @return 结果
     */
    public static boolean internalIp(final String ip) {
        final byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || LOCALHOST_IPV4.equals(ip);
    }

    /**
     * 检查是否为内部IP地址
     *
     * @param ip byte地址
     * @return 结果
     */
    private static boolean internalIp(final byte[] ip) {
        if (Objects.isNull(ip) || ip.length < 2) {
            return true;
        }
        switch (ip[0] & 0xFF) {
            case 10:
                // A类私有IP地址
                return true;
            case 172:
                // B类私有IP地址
                return (ip[1] & 0xFF) >= 16 && (ip[1] & 0xFF) <= 31;
            case 192:
                // C类私有IP地址
                return (ip[1] & 0xFF) == 168;
            case 127:
                // 本地回环地址
                return true;
            default:
                return false;
        }
    }

    /**
     * 将IPv4地址转换成字节
     *
     * @param ipAddress IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(final String ipAddress) {
        try {
            final InetAddress address = InetAddress.getByName(ipAddress);
            return address.getAddress();
        } catch (final UnknownHostException e) {
            LOGGER.error("IPv4地址转换失败{}：", ipAddress, e);
            return new byte[0];
        }
    }

    /**
     * 获取IP地址
     *
     * @return 本地IP地址
     */
    public static String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (final UnknownHostException e) {
            LOGGER.error("获取ip失败：", e);
        }
        return LOCALHOST_IPV4;
    }

    /**
     * 获取主机名
     *
     * @return 本地主机名
     */
    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (final UnknownHostException e) {
            LOGGER.error("获取本地主机名失败：", e);
        }
        return "未知";
    }

    /**
     * 从多级反向代理中获得第一个非unknown IP地址
     *
     * @param ip 获得的IP地址
     * @return 第一个非unknown IP地址
     */
    public static String getMultistageReverseProxyIp(final String ip) {
        // 多级反向代理检测
        if (ip != null && ip.contains(",")) {
            final String[] ips = StringUtils.split(ip.trim(), ",");
            for (final String subIp : ips) {
                if (isValidHeaderIp(subIp)) {
                    return StringUtils.substring(subIp, 0, 255);
                }
            }
        }
        return StringUtils.substring(ip, 0, 255);
    }

    /**
     * 检测给定字符串是否为有效IP地址
     *
     * @param headerIp 被检测的字符串
     * @return 是否有效IP地址
     */
    private static boolean isValidHeaderIp(String headerIp) {
        return StringUtils.isNotBlank(headerIp) && !UNKNOWN.equalsIgnoreCase(headerIp);
    }


    /**
     * 是否为IP
     */
    public static boolean isIP(final String ip) {
        return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
    }

    /**
     * 是否为IP，或 *为间隔的通配符地址
     */
    public static boolean isIpWildCard(final String ip) {
        return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
    }

    /**
     * 检测参数是否在ip通配符里
     */
    public static boolean ipIsInWildCardNoCheck(final String ipWildCard, final String ip) {
        final String[] s1 = StringUtils.split(ipWildCard, ".");
        final String[] s2 = StringUtils.split(ip, ".");
        for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) {
            if (!s1[i].equals(s2[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
     */
    public static boolean isIPSegment(final String ipSeg) {
        return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
    }

    /**
     * 判断ip是否在指定网段中
     */
    public static boolean ipIsInNetNoCheck(final String ipArea, final String ip) {
        final int idx = ipArea.indexOf('-');
        // ip 的起始端（取.分隔成数组）
        final String[] sips = StringUtils.split(ipArea.substring(0, idx), ".");
        // ip 的结束端（取.分隔成数组）
        final String[] sipe = StringUtils.split(ipArea.substring(idx + 1), ".");
        // 当前ip（取.分隔成数组）
        final String[] sipt = StringUtils.split(ip, ".");
        long ips = 0L;
        long ipe = 0L;
        long ipt = 0L;
        for (int i = 0; i < 4; ++i) {
            ips = ips << 8 | Integer.parseInt(sips[i]);
            ipe = ipe << 8 | Integer.parseInt(sipe[i]);
            ipt = ipt << 8 | Integer.parseInt(sipt[i]);
        }
        if (ips > ipe) {
            final long t = ips;
            ips = ipe;
            ipe = t;
        }
        return ips <= ipt && ipt <= ipe;
    }

    /**
     * 校验ip是否符合过滤串规则
     *
     * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
     * @param ip 校验IP地址
     * @return boolean 结果
     */
    public static boolean isMatchedIp(final String filter, final String ip) {
        if (StringUtils.isAnyEmpty(filter, ip)) {
            return false;
        }

        return Arrays.stream(StringUtils.split(filter, ";"))
            .anyMatch(iStr -> (isIP(iStr) && iStr.equals(ip)) ||
                (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) ||
                (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)));
    }
}
